package com.spzx.channel.service.impl;

import com.alibaba.fastjson2.JSON;
import com.spzx.channel.domain.vo.ItemVO;
import com.spzx.channel.domain.vo.SkuPriceVO;
import com.spzx.channel.domain.vo.SpecValueVO;
import com.spzx.channel.service.ItemService;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.StringUtils;
import com.spzx.common.core.utils.ThreadPoolUtils;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.common.redis.cache.SpzxCache;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.RemoteSkuStockService;
import com.spzx.product.domain.*;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class ItemServiceImpl implements ItemService {
    @Resource
    RemoteProductService remoteProductService;
    @Resource
    RemoteSkuStockService remoteSkuStockService;
    @Resource
    RedisTemplate redisTemplate;
    //缓存管理：
    //业务：需要查询前端需要的数据
    //    前端需要的数据我们创建了ItemVo映射
    //    业务只需要查询ItemVO需要的数据即可
//    @Cacheable( value = "spzx:channel:cache"  , key ="'item:'+#skuId" ) //value和key拼接起来 表示缓存的键 ，key中可以使用springEL表达式获取形参
    //上面的缓存key为： spzx:channel:cache::item:1       skuId的值
   @SpzxCache(cacheKeyPrefix = "spzx:mycache:" , lockKeyPrefix = "spzx:channel:lock:",
            isCacheNull = true ,
            bitmapCheck = true ,
           bitmapIndex = 0,
           bitmapKey = "spzx:sku:ids"
   )
    @Override
    public ItemVO selectItemBySkuId(Long skuId) {
        // 拼接缓存的key
//        String cacheKey = "spzx:channel:item:"+skuId;
//        //==1、查询是否存在缓存，如果存在 直接返回缓存
//        Object cache = redisTemplate.opsForValue()
//                .get(cacheKey);
//        if(cache!=null){
//            return (ItemVO) cache;
//        }
        //==2、如果没有缓存，执行下面的业务 远程调用查询数据 组装数据
        //查询spu和sku时，是会员查询的，只能查询 已审核已上架的商品！！！！
        ItemVO itemVO = new ItemVO();
       CompletableFuture<ProductSku> cf1 = CompletableFuture.supplyAsync(() -> {
           //1、根据skuId查询sku表中的数据
           //远程调用内部接口 需要在请求头中配置 from-source = inner的字符串
           // 解决： 通过feign客户端的 请求拦截器 全局配置！！
           R<ProductSku> skuR = remoteProductService.getSkuBySkuId(skuId);
           if (skuR.getCode() != 200) {
               throw new ServiceException("商品数据不存在！！");
           }
           ProductSku sku = skuR.getData();
           itemVO.setProductSku(sku);
           //封装价格数据
           SkuPriceVO skuPriceVO = new SkuPriceVO();
           skuPriceVO.setSkuId(skuId);
           skuPriceVO.setMarketPrice(sku.getMarketPrice());
           skuPriceVO.setSalePrice(sku.getSalePrice());
           itemVO.setSkuPrice(skuPriceVO);
           return sku; //3，4，5需要使用sku中的productId
       }, ThreadPoolUtils.getPool());

       //2、根据skuId查询sku_stock中的库存数据
       CompletableFuture<Void> cf2 = CompletableFuture.runAsync(() -> {
           //spzx-product需要提供远程调用接口1： 根据skuId查询库存数据
           R<SkuStock> skuStockR = remoteSkuStockService.getSkuStock(skuId);
           if (skuStockR.getCode() != 200) {
               throw new ServiceException("商品库存数据异常!");
           }
           SkuStock skuStock = skuStockR.getData();
           SkuStockVo skuStockVo = new SkuStockVo();
//        skuStockVo.setSkuId(skuStock.getSkuId());
//        skuStockVo.setSaleNum(skuStock.getSaleNum());
//        skuStockVo.setAvailableNum(skuStock.getAvailableNum());
           BeanUtils.copyProperties(skuStock, skuStockVo); //将skuStock对象的相同名称类型的属性拷贝给 skuStockVo对象
           itemVO.setSkuStockVo(skuStockVo);
       }, ThreadPoolUtils.getPool());


       //3、根据1查询到的sku的productId查询product数据
       CompletableFuture<Void> cf1_1 = cf1.thenAcceptAsync((sku) -> {
           Long productId = sku.getProductId();
           //spzx-product需要提供远程调用接口2： 根据productId查询product详情
           R<Product> productR = remoteProductService.getSpu(productId);
           if (productR.getCode() != 200) {
               throw new ServiceException("商品数据异常!");
           }
           Product product = productR.getData();
           //===
           itemVO.setProduct(product);
           //http://192.168.123.100:9000/spzx/2024/12/21/5fcdce071193d65e_20241221135417A005.jpg,http://192.168.123.100:9000/spzx/2024/12/21/143015346a7181ce_20241221135417A006.jpg,http://192.168.123.100:9000/spzx/2024/12/21/4f0cba55568b40fb_20241221135417A007.jpg,http://192.168.123.100:9000/spzx/2024/12/21/1589d3f5a503f4f0_20241221135417A008.png
           List<String> sliderUrls = StringUtils.isEmpty(product.getSliderUrls()) ? null :
                   Arrays.asList(product.getSliderUrls().split(","));
           //轮播图：     //===
           itemVO.setSliderUrlList(sliderUrls);
           //[{"key":"颜色","valueList":["白色","红色","黑色"]},{"key":"内存","valueList":["8G","18G"]}]
           String specValue = product.getSpecValue();
           //将specValue json数组 转为 specValueVo对象的 集合
           List<SpecValueVO> specValueVOS = JSON.parseArray(specValue, SpecValueVO.class);
           //product的规格值  ===
           itemVO.setSpecValueList(specValueVOS);
       }, ThreadPoolUtils.getPool());


       //4、根据1查询到的sku的productId查询product_details表 详情图列表
        //spzx-product需要提供远程调用接口3： 根据productId查询productDetails详情图列表数据
       CompletableFuture<Void> cf1_2 = cf1.thenAcceptAsync((sku) -> {
           Long productId = sku.getProductId();
           R<ProductDetails> productDetailsR = remoteProductService.getDetails(productId);
           if (productDetailsR.getCode() == 200) {
               ProductDetails productDetails = productDetailsR.getData();
               String imageUrls = productDetails.getImageUrls();
               List<String> detailsimagesUrls = StringUtils.isEmpty(imageUrls) ? null :
                       Arrays.asList(imageUrls.split(","));

//               itemVO.getProduct().setDetailsImageUrlList(detailsimagesUrls);
               itemVO.setDetailsImageUrlList(detailsimagesUrls);
           }
       }, ThreadPoolUtils.getPool());
       //5、根据1查询到的sku的productId查询 sku表spu下所有的sku列表
        // spzx-product需要提供远程调用接口4：根据productId查询它所有的sku列表
       CompletableFuture<Void> cf1_3 = cf1.thenAcceptAsync((sku) -> {
           Long productId = sku.getProductId();
           R<List<ProductSku>> skusR = remoteProductService.getSkus(productId);
           if (skusR.getCode() != 200) {
               throw new ServiceException(skusR.getMsg());
           }

           List<ProductSku> skus = skusR.getData();

           //{"黑色 + 18G": 6, "红色 + 18G": 4} => 使用每个sku的skuSpec映射skuId
           //遍历skus列表，将每个sku的 specValue当作键，id当作值存到一个map中
//        Map<String, Long> map = skus.stream().collect(Collectors.toMap(ProductSku::getSkuSpec, ProductSku::getId));
           Map<String, String> map = new HashMap<>();
           skus.forEach(productSku -> {
               map.put(productSku.getSkuSpec(), productSku.getId().toString());
           });
           itemVO.setSkuSpecValueMap(map);
       }, ThreadPoolUtils.getPool());


       //===3、缓存查询到的组装好的数据到redis

        //设置缓存 并配置过期时间600秒！
//        redisTemplate.opsForValue().set(cacheKey,itemVO,600 , TimeUnit.SECONDS);
       //返回结果之前 必须保证异步编排任务都执行结束
       CompletableFuture.allOf(cf1,cf2, cf1_1  ,cf1_2 , cf1_3).join();
        return itemVO;
    }
}
